package cc.shacocloud.mirage.utils;

import cc.shacocloud.mirage.utils.collection.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.function.Supplier;

/**
 * 常用的工具类
 */
public class Utils {
    
    private static final int INITIAL_HASH = 7;
    private static final int MULTIPLIER = 31;
    
    private static final String EMPTY_STRING = "";
    private static final String NULL_STRING = "null";
    private static final String ARRAY_START = "[";
    private static final String ARRAY_END = "]";
    private static final String EMPTY_ARRAY = ARRAY_START + ARRAY_END;
    private static final String ARRAY_ELEMENT_SEPARATOR = ", ";
    private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
    
    /**
     * 将给定的数组（可能是基元数组）转换为对象数组（如果需要基元包装器对象）。
     * <p>
     * {@code null} 源值将转换为空的对象数组。
     *
     * @param source （可能原始的）数组
     * @return 相应的对象数组（从不 {@code null}）
     * @throws IllegalArgumentException 如果参数不是数组
     */
    public static Object[] toObjectArray(@Nullable Object source) {
        if (source instanceof Object[]) {
            return (Object[]) source;
        }
        if (source == null) {
            return EMPTY_OBJECT_ARRAY;
        }
        if (!source.getClass().isArray()) {
            throw new IllegalArgumentException("source 不是数组类型：" + source);
        }
        int length = Array.getLength(source);
        if (length == 0) {
            return EMPTY_OBJECT_ARRAY;
        }
        Class<?> wrapperType = Array.get(source, 0).getClass();
        Object[] newArray = (Object[]) Array.newInstance(wrapperType, length);
        for (int i = 0; i < length; i++) {
            newArray[i] = Array.get(source, i);
        }
        return newArray;
    }
    
    /**
     * 确定给定的对象是否相等，如果两个对象都是 {@code null}，则返回 {@code true};如果只有一个对象为 {@code null}，则返回 {@code false}
     * <p>
     * 将数组与 {@code Arrays.equals} 进行比较，根据数组元素而不是数组引用执行相等性检查。
     *
     * @param o1 第一个要比较的对象
     * @param o2 第二个要比较的对象
     * @return 给定对象是否相等
     * @see Object#equals(Object)
     * @see java.util.Arrays#equals
     */
    public static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
        if (o1 == o2) {
            return true;
        }
        if (o1 == null || o2 == null) {
            return false;
        }
        if (o1.equals(o2)) {
            return true;
        }
        if (o1.getClass().isArray() && o2.getClass().isArray()) {
            return ArrayUtil.equals(o1, o2);
        }
        return false;
    }
    
    /**
     * 如果值为空则使用提供者函数的值
     */
    public static <T extends CharSequence> T emptyOrDefault(@Nullable T value, Supplier<T> supplier) {
        if (Objects.isNull(value) || value.length() == 0) {
            return supplier.get();
        }
        return value;
    }
    
    /**
     * 如果值为空则使用提供者函数提供的值
     */
    public static <T> T nullOrDefault(T value, Supplier<T> supplier) {
        if (Objects.isNull(value)) {
            return supplier.get();
        }
        return value;
    }
    
    /**
     * 方法描述
     *
     * @param method 待描述的方法
     * @return 描述字符
     */
    public static @NotNull String methodDescription(@NotNull Method method) {
        return methodDescription(method.getDeclaringClass(), method);
    }
    
    /**
     * 方法描述
     *
     * @param beanType 待描述的方法所声明的类
     * @param method   待描述的方法
     * @return 描述字符
     */
    public static @NotNull String methodDescription(@NotNull Class<?> beanType, @NotNull Method method) {
        StringJoiner joiner = new StringJoiner(", ", "(", ")");
        for (Class<?> paramType : method.getParameterTypes()) {
            joiner.add(paramType.getSimpleName());
        }
        return beanType.getName() + "#" + method.getName() + joiner;
    }
    
    /**
     * 作为给定对象的哈希代码返回;通常是 {@code ObjecthashCode（）}} 的值
     * 如果对象是数组，则此方法将委托给此类中数组的任何 {@code nullSafeHashCode} 方法
     * 如果对象为 {@code null}，则此方法返回 0
     *
     * @see Object#hashCode()
     * @see #nullSafeHashCode(Object[])
     * @see #nullSafeHashCode(boolean[])
     * @see #nullSafeHashCode(byte[])
     * @see #nullSafeHashCode(char[])
     * @see #nullSafeHashCode(double[])
     * @see #nullSafeHashCode(float[])
     * @see #nullSafeHashCode(int[])
     * @see #nullSafeHashCode(long[])
     * @see #nullSafeHashCode(short[])
     */
    public static int nullSafeHashCode(@Nullable Object obj) {
        if (obj == null) {
            return 0;
        }
        if (obj.getClass().isArray()) {
            if (obj instanceof Object[]) {
                return nullSafeHashCode((Object[]) obj);
            }
            if (obj instanceof boolean[]) {
                return nullSafeHashCode((boolean[]) obj);
            }
            if (obj instanceof byte[]) {
                return nullSafeHashCode((byte[]) obj);
            }
            if (obj instanceof char[]) {
                return nullSafeHashCode((char[]) obj);
            }
            if (obj instanceof double[]) {
                return nullSafeHashCode((double[]) obj);
            }
            if (obj instanceof float[]) {
                return nullSafeHashCode((float[]) obj);
            }
            if (obj instanceof int[]) {
                return nullSafeHashCode((int[]) obj);
            }
            if (obj instanceof long[]) {
                return nullSafeHashCode((long[]) obj);
            }
            if (obj instanceof short[]) {
                return nullSafeHashCode((short[]) obj);
            }
        }
        return obj.hashCode();
    }
    
    /**
     * 根据指定数组的内容返回哈希代码。如果 {@code 数组} 为 {@code null}，则此方法返回 0。
     */
    public static int nullSafeHashCode(@Nullable Object[] array) {
        if (array == null) {
            return 0;
        }
        int hash = INITIAL_HASH;
        for (Object element : array) {
            hash = MULTIPLIER * hash + nullSafeHashCode(element);
        }
        return hash;
    }
    
    /**
     * 根据指定数组的内容返回哈希代码。如果 {@code 数组} 为 {@code null}，则此方法返回 0
     */
    public static int nullSafeHashCode(boolean @Nullable [] array) {
        if (array == null) {
            return 0;
        }
        int hash = INITIAL_HASH;
        for (boolean element : array) {
            hash = MULTIPLIER * hash + Boolean.hashCode(element);
        }
        return hash;
    }
    
    /**
     * 根据指定数组的内容返回哈希代码。如果 {@code 数组} 为 {@code null}，则此方法返回 0
     */
    public static int nullSafeHashCode(byte @Nullable [] array) {
        if (array == null) {
            return 0;
        }
        int hash = INITIAL_HASH;
        for (byte element : array) {
            hash = MULTIPLIER * hash + element;
        }
        return hash;
    }
    
    /**
     * 根据指定数组的内容返回哈希代码。如果 {@code 数组} 为 {@code null}，则此方法返回 0
     */
    public static int nullSafeHashCode(char @Nullable [] array) {
        if (array == null) {
            return 0;
        }
        int hash = INITIAL_HASH;
        for (char element : array) {
            hash = MULTIPLIER * hash + element;
        }
        return hash;
    }
    
    /**
     * 根据指定数组的内容返回哈希代码。如果 {@code 数组} 为 {@code null}，则此方法返回 0
     */
    public static int nullSafeHashCode(double @Nullable [] array) {
        if (array == null) {
            return 0;
        }
        int hash = INITIAL_HASH;
        for (double element : array) {
            hash = MULTIPLIER * hash + Double.hashCode(element);
        }
        return hash;
    }
    
    /**
     * 根据指定数组的内容返回哈希代码。如果 {@code 数组} 为 {@code null}，则此方法返回 0
     */
    public static int nullSafeHashCode(float @Nullable [] array) {
        if (array == null) {
            return 0;
        }
        int hash = INITIAL_HASH;
        for (float element : array) {
            hash = MULTIPLIER * hash + Float.hashCode(element);
        }
        return hash;
    }
    
    /**
     * 根据指定数组的内容返回哈希代码。如果 {@code 数组} 为 {@code null}，则此方法返回 0
     */
    public static int nullSafeHashCode(int @Nullable [] array) {
        if (array == null) {
            return 0;
        }
        int hash = INITIAL_HASH;
        for (int element : array) {
            hash = MULTIPLIER * hash + element;
        }
        return hash;
    }
    
    /**
     * 根据指定数组的内容返回哈希代码。如果 {@code 数组} 为 {@code null}，则此方法返回 0
     */
    public static int nullSafeHashCode(long @Nullable [] array) {
        if (array == null) {
            return 0;
        }
        int hash = INITIAL_HASH;
        for (long element : array) {
            hash = MULTIPLIER * hash + Long.hashCode(element);
        }
        return hash;
    }
    
    /**
     * 根据指定数组的内容返回哈希代码。如果 {@code 数组} 为 {@code null}，则此方法返回 0
     */
    public static int nullSafeHashCode(short @Nullable [] array) {
        if (array == null) {
            return 0;
        }
        int hash = INITIAL_HASH;
        for (short element : array) {
            hash = MULTIPLIER * hash + element;
        }
        return hash;
    }
    
    /**
     * 返回指定对象的一个字符串表示
     */
    public static String nullSafeToString(@Nullable Object obj) {
        if (obj == null) {
            return NULL_STRING;
        }
        if (obj instanceof String) {
            return (String) obj;
        }
        if (obj instanceof Object[]) {
            return nullSafeToString((Object[]) obj);
        }
        if (obj instanceof boolean[]) {
            return nullSafeToString((boolean[]) obj);
        }
        if (obj instanceof byte[]) {
            return nullSafeToString((byte[]) obj);
        }
        if (obj instanceof char[]) {
            return nullSafeToString((char[]) obj);
        }
        if (obj instanceof double[]) {
            return nullSafeToString((double[]) obj);
        }
        if (obj instanceof float[]) {
            return nullSafeToString((float[]) obj);
        }
        if (obj instanceof int[]) {
            return nullSafeToString((int[]) obj);
        }
        if (obj instanceof long[]) {
            return nullSafeToString((long[]) obj);
        }
        if (obj instanceof short[]) {
            return nullSafeToString((short[]) obj);
        }
        String str = obj.toString();
        return (str != null ? str : EMPTY_STRING);
    }
    
    /**
     * 返回指定数组内容的一个字符串表示
     */
    public static String nullSafeToString(@Nullable Object[] array) {
        if (array == null) {
            return NULL_STRING;
        }
        int length = array.length;
        if (length == 0) {
            return EMPTY_ARRAY;
        }
        StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
        for (Object o : array) {
            stringJoiner.add(String.valueOf(o));
        }
        return stringJoiner.toString();
    }
    
    /**
     * 返回指定数组内容的一个字符串表示
     */
    public static String nullSafeToString(boolean[] array) {
        if (array == null) {
            return NULL_STRING;
        }
        int length = array.length;
        if (length == 0) {
            return EMPTY_ARRAY;
        }
        StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
        for (boolean b : array) {
            stringJoiner.add(String.valueOf(b));
        }
        return stringJoiner.toString();
    }
    
    /**
     * 返回指定数组内容的一个字符串表示
     */
    public static String nullSafeToString(byte @Nullable [] array) {
        if (array == null) {
            return NULL_STRING;
        }
        int length = array.length;
        if (length == 0) {
            return EMPTY_ARRAY;
        }
        StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
        for (byte b : array) {
            stringJoiner.add(String.valueOf(b));
        }
        return stringJoiner.toString();
    }
    
    /**
     * 返回指定数组内容的一个字符串表示
     */
    public static String nullSafeToString(char[] array) {
        if (array == null) {
            return NULL_STRING;
        }
        int length = array.length;
        if (length == 0) {
            return EMPTY_ARRAY;
        }
        StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
        for (char c : array) {
            stringJoiner.add('\'' + String.valueOf(c) + '\'');
        }
        return stringJoiner.toString();
    }
    
    /**
     * 返回指定数组内容的一个字符串表示
     */
    public static String nullSafeToString(double[] array) {
        if (array == null) {
            return NULL_STRING;
        }
        int length = array.length;
        if (length == 0) {
            return EMPTY_ARRAY;
        }
        StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
        for (double d : array) {
            stringJoiner.add(String.valueOf(d));
        }
        return stringJoiner.toString();
    }
    
    /**
     * 返回指定数组内容的一个字符串表示
     */
    public static String nullSafeToString(float[] array) {
        if (array == null) {
            return NULL_STRING;
        }
        int length = array.length;
        if (length == 0) {
            return EMPTY_ARRAY;
        }
        StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
        for (float f : array) {
            stringJoiner.add(String.valueOf(f));
        }
        return stringJoiner.toString();
    }
    
    /**
     * 返回指定数组内容的一个字符串表示
     */
    public static String nullSafeToString(int[] array) {
        if (array == null) {
            return NULL_STRING;
        }
        int length = array.length;
        if (length == 0) {
            return EMPTY_ARRAY;
        }
        StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
        for (int i : array) {
            stringJoiner.add(String.valueOf(i));
        }
        return stringJoiner.toString();
    }
    
    /**
     * 返回指定数组内容的一个字符串表示
     */
    public static String nullSafeToString(long[] array) {
        if (array == null) {
            return NULL_STRING;
        }
        int length = array.length;
        if (length == 0) {
            return EMPTY_ARRAY;
        }
        StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
        for (long l : array) {
            stringJoiner.add(String.valueOf(l));
        }
        return stringJoiner.toString();
    }
    
    /**
     * 返回指定数组内容的一个字符串表示
     */
    public static String nullSafeToString(short @Nullable [] array) {
        if (array == null) {
            return NULL_STRING;
        }
        int length = array.length;
        if (length == 0) {
            return EMPTY_ARRAY;
        }
        StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
        for (short s : array) {
            stringJoiner.add(String.valueOf(s));
        }
        return stringJoiner.toString();
    }
    
    /**
     * 解开给定对象，该对象可能是 {@link java.util.Optional}。
     *
     * @param obj 候选对象
     * @return 在 {@code Optional} 中保存的值，如果 {@code Optional} 为空，则为 {@code null}，或者只是按原样给定对象
     */
    @Nullable
    public static Object unwrapOptional(@Nullable Object obj) {
        if (obj instanceof Optional) {
            Optional<?> optional = (Optional<?>) obj;
            if (optional.isEmpty()) {
                return null;
            }
            
            Object result = optional.get();
            return unwrapOptional(result);
        }
        return obj;
    }
}
